package com.appistry.api.rest.httpcommons;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.httpclient.methods.RequestEntity;

/**
 * A variation of the original
 * {@link org.apache.commons.httpclient.methods.InputStreamRequestEntity
 * InputStreamRequestEntity}. The key difference is that it does not buffer to
 * determine unknown content length.
 * 
 * @since 3.0
 */
public class InputStreamRequestEntity implements RequestEntity {

	/**
	 * The content length will not be calculated.
	 */
	public static final int UNKNOWN_CONTENT_LENGTH = -1;

	private long contentLength;

	private InputStream content;

	/** The content type */
	private String contentType;

	/**
	 * Creates a new InputStreamRequestEntity with the given content and a
	 * content type of {@link #UNKNOWN_CONTENT_LENGTH}.
	 * 
	 * @param content
	 *            The content to set.
	 */
	public InputStreamRequestEntity(InputStream content) {
		this(content, null);
	}

	/**
	 * Creates a new InputStreamRequestEntity with the given content, content
	 * type, and a content length of {@link #UNKNOWN_CONTENT_LENGTH}.
	 * 
	 * @param content
	 *            The content to set.
	 * @param contentType
	 *            The type of the content, or <code>null</code>.
	 */
	public InputStreamRequestEntity(InputStream content, String contentType) {
		this(content, UNKNOWN_CONTENT_LENGTH, contentType);
	}

	/**
	 * Creates a new InputStreamRequestEntity with the given content and content
	 * length.
	 * 
	 * @param content
	 *            The content to set.
	 * @param contentLength
	 *            The content size in bytes or a negative number if not known.
	 */
	public InputStreamRequestEntity(InputStream content, long contentLength) {
		this(content, contentLength, null);
	}

	/**
	 * Creates a new InputStreamRequestEntity with the given content, content
	 * length, and content type.
	 * 
	 * @param content
	 *            The content to set.
	 * @param contentLength
	 *            The content size in bytes or a negative number if not known.
	 * @param contentType
	 *            The type of the content, or <code>null</code>.
	 */
	public InputStreamRequestEntity(InputStream content, long contentLength, String contentType) {
		if (content == null) {
			throw new IllegalArgumentException("The content cannot be null");
		}
		this.content = content;
		this.contentLength = contentLength;
		this.contentType = contentType;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.commons.httpclient.methods.RequestEntity#getContentType()
	 */
	public String getContentType() {
		return contentType;
	}

	/**
	 * Tests if this method is repeatable. Only <code>true</code> if the content
	 * has been buffered.
	 * 
	 * @see #getContentLength()
	 */
	public boolean isRepeatable() {
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.commons.httpclient.RequestEntity#writeRequest(java.io.OutputStream
	 * )
	 */
	public void writeRequest(OutputStream out) throws IOException {

		if (content != null) {
			byte[] tmp = new byte[4096];
			int total = 0;
			int i = 0;
			while ((i = content.read(tmp)) >= 0) {
				out.write(tmp, 0, i);
				total += i;
			}
		} else {
			throw new IllegalStateException("Content must be set before entity is written");
		}
	}

	/**
	 * Gets the content length. If the content length has not been set, this
	 * will return -1
	 */
	public long getContentLength() {
		return contentLength;
	}

	/**
	 * @return Returns the content.
	 */
	public InputStream getContent() {
		return content;
	}

}
